package com.mazaiting.instructions.base;

import com.mazaiting.util.ByteUtil;
import com.mazaiting.util.UnsignedNumberUtil;
import lombok.Getter;

/**
 * 指令字节码读取工具
 *
 * @author mazaiting
 * @since 2021/8/14
 */
@Getter
public class BytecodeReader {
    /**
     * 二进制方法体
     * 存放字节码
     */
    private byte[] code;
    /**
     * 计数
     * 段记录读取到了哪个字节
     */
    private int pc;

    /**
     * 避免每次解码指令都新创建一个BytecodeReader实例
     * @param code 存放字节码
     * @param pc 段记录读取到了哪个字节
     */
    public void reset(byte[] code, int pc) {
        this.code = code;
        this.pc = pc;
    }

    /**
     * 读取一个字节数
     * @return 一个字节有符号数
     */
    private byte readByte() {
        byte b = code[pc];
        this.pc++;
        return b;
    }

    /***
     * 读取一个字节
     * @return 一个字节数
     */
    public byte readU1() {
        return readByte();
    }

    /***
     * 读取两个字节
     * @return 两个字节数
     */
    public short readU2() {
        byte[] bytes = new byte[]{readByte(), readByte()};
        return ByteUtil.byteToShort(bytes);
    }

    /***
     * 读取四个字节
     * @return 四个字节数
     */
    public int readU4() {
        byte[] bytes = new byte[]{readByte(), readByte(), readByte(), readByte()};
        return ByteUtil.byteToInt(bytes);
    }

    /***
     * 读取一个字节
     * @return 转换为无符号一个字节数
     */
    public short readU1ToUnsignedByte() {
        return UnsignedNumberUtil.toUnsignedByteValue(readByte());
    }

    /**
     * 读取两个字节的 short 类型数据
     * @return 无符号 short 类型数据
     */
    public int readU2ToUnsignedShort() {
        byte[] bytes = new byte[]{readByte(), readByte()};
        return UnsignedNumberUtil.toUnsignedShortValue(ByteUtil.byteToShort(bytes));
    }

    /**
     * 读取四个字节的 int 整型
     * @return 无符号整型数据
     */
    public long readU4ToUnsignedInt() {
        byte[] bytes = new byte[]{readByte(), readByte(), readByte(), readByte()};
        return UnsignedNumberUtil.toUnsignedIntValue(ByteUtil.byteToInt(bytes));
    }

    /**
     * 读取四个字节数组
     * @param count 读取数量
     * @return 四个字节数组
     */
    public int[] readU4s(int count) {
        int[] nums = new int[count];
        for (int i = 0; i < count; i++) {
            nums[i] = readU4();
        }
        return nums;
    }

    /**
     * 跳过填充
     */
    public void skipPadding() {
        while (this.pc % 4 == 0) {
            readU1();
        }
    }
}
